/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <memory>
#include <mutex>
#include "kh_sle_announce.h"
#include "kh_sle_host.h"
#include "sle_announce_interface.h"
#include "sle_hilog.h"

#ifdef __cplusplus
extern "C" {
#endif

namespace OHOS {
namespace NearLink {
namespace SleStandard {
#define SLE_MAX_ANNOUNCE_CALLBACK_NUM  3

static SleAnnounce *g_announceInstance;
static SleAnnounceCallbacks* g_announceCallbacks[SLE_MAX_ANNOUNCE_CALLBACK_NUM] = {nullptr};
static std::mutex g_announceCbLock;
static bool g_isAnnounceInited = false;

class SleAnnounceCallbackWapper : public SleAnnounceCallback {
public:
    SleAnnounceCallbackWapper(){};

    void OnAnnounceStateChange(uint8_t announceId, SleAnnounceState announceState, bool terminal) override
    {
        std::lock_guard<std::mutex> lock(g_announceCbLock);
        for (int i = 0; i < SLE_MAX_ANNOUNCE_CALLBACK_NUM; i++) {
            if (g_announceCallbacks[i] != nullptr && g_announceCallbacks[i]->OnSleAnnounceStateChangeCb != nullptr) {
                g_announceCallbacks[i]->OnSleAnnounceStateChangeCb(announceId, announceState, terminal);
            }
        }
    }
};

static std::shared_ptr<SleAnnounceCallbackWapper> g_announceCbWapper;

SleErrorCode CheckAnnounceOperation()
{
    SLE_ASSERT_AND_RETURN_RET_LOG(g_isAnnounceInited == true, SLE_ERR_INTERNAL_WRONG, "announce service not inited");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_announceInstance, SLE_ERR_INTERNAL_WRONG, "announce instance null");
    return SLE_NO_ERROR;
}

SleErrorCode InitSleAnnounceService()
{
    SLE_LOGI("InitSleAnnounceService In");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_isAnnounceInited == false, SLE_NO_ERROR, "announce already inited error!");
    g_announceCbWapper = std::make_shared<SleAnnounceCallbackWapper>();
    SLE_ASSERT_AND_RETURN_RET_LOG(g_announceCbWapper, SLE_ERR_FAILED, "callback wapper error!");
    if (g_announceInstance == nullptr) {
        g_announceInstance = new SleAnnounce();
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(g_announceInstance, SLE_ERR_FAILED, "announce manager instance error!");
    SleErrorCode ret = g_announceInstance->RegisterAnnounceCallback(g_announceCbWapper);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "init announce service failed!");
    g_isAnnounceInited = true;
    return SLE_NO_ERROR;
}

SleErrorCode DeinitSleAnnounceService()
{
    SLE_LOGI("DeinitSleAnnounceService In");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_isAnnounceInited == true, SLE_NO_ERROR, "announce not inited error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(g_announceCbWapper, SLE_ERR_FAILED, "callback wapper error!");
    SleErrorCode ret = g_announceInstance->UnregisterAnnounceCallback(g_announceCbWapper);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "deinit announce service failed!");
    if (g_announceInstance != nullptr) {
        delete g_announceInstance;
        g_announceInstance = nullptr;
    }
    g_isAnnounceInited = false;
    return SLE_NO_ERROR;
}

SleErrorCode AddAnnounce(uint8_t *announceId)
{
    SLE_LOGI("AddAnnounce In");
    SLE_ASSERT_AND_RETURN_RET_LOG(announceId != nullptr, SLE_ERR_INVALID_PARAM, "announce id param error!");
    SleErrorCode ret = CheckAnnounceOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = g_announceInstance->AddAnnounce(*announceId);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "add announce failed!");
    return SLE_NO_ERROR;
}

SleErrorCode RemoveAnnounce(uint8_t announceId)
{
    SLE_LOGI("RemoveAnnounce In");
    if (announceId < 0 || announceId > SLE_MAX_ANNOUNCE_ID_NUM) {
        SLE_LOGE("param announce id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    SleErrorCode ret = CheckAnnounceOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = g_announceInstance->RemoveAnnounce(announceId);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "remove announce failed!");
    return SLE_NO_ERROR;
}

SleErrorCode StartAnnounce(uint8_t announceId, SleAnnounceData *data, const SleAnnounceParam *param)
{
    SLE_LOGI("StartAnnounce In");
    if (announceId < 0 || announceId > SLE_MAX_ANNOUNCE_ID_NUM) {
        SLE_LOGE("param announce id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    SleErrorCode ret = CheckAnnounceOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SLE_ASSERT_AND_RETURN_RET_LOG(data, SLE_ERR_INVALID_PARAM, "announce data error!");
    if(param == NULL) {
        SleAnnounceParam defaultParam = {
            .handle = 1,
            .mode = DEFAULT_SLE_ADV_MODE,
            .role = DEFAULT_SLE_ADV_ROLE,
            .level = DEFAULT_SLE_ADV_LEVEL,
            .channelMap = DEFAULT_SLE_ADV_CHANNEL_MAP,
            .annonceIntervalMin = DEFAULT_SLE_ADV_INTERVAL_MIN,
            .annonceIntervalMax = DEFAULT_SLE_ADV_INTERVAL_MAX,
            .txPower = DEFAULT_SLE_ADV_TX_POWER,
            .connectIntervalMin = DEFAULT_SLE_ADV_CONN_INTV_MIN,
            .connectIntervalMax = DEFAULT_SLE_ADV_CONN_INTV_MAX,
            .connectLatency = DEFAULT_SLE_ADV_CONN_AX_LATENCY,
            .connectTimeout = DEFAULT_SLE_ADV_CONN_SUPERVISION_TIMEOUT,
        };
        if(param != NULL) {
            memcpy(&defaultParam, param, sizeof(SleAnnounceParam));
        }
        ret = g_announceInstance->StartAnnounce(announceId, *data, defaultParam);
    } else {
        ret = g_announceInstance->StartAnnounce(announceId, *data, *param);
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "start announce failed!");
    return SLE_NO_ERROR;
}

SleErrorCode StopAnnounce(uint8_t announceId)
{
    SLE_LOGI("StopAnnounce In");
    if (announceId < 0 || announceId > SLE_MAX_ANNOUNCE_ID_NUM) {
        SLE_LOGE("param announce id error!");
        return SLE_ERR_INVALID_PARAM;
    }
    SleErrorCode ret = CheckAnnounceOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = g_announceInstance->StopAnnounce(announceId);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "stop announce failed!");
    return SLE_NO_ERROR;
}

SleErrorCode GetDefaultAnnounceParamConfig(SleAnnounceParam *announceConfig)
{
    SLE_LOGI("GetDefaultAnnounceParamConfig");
    SLE_ASSERT_AND_RETURN_RET(announceConfig != NULL, SLE_ERR_INVALID_PARAM);
    SleErrorCode ret = CheckAnnounceOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleAnnounceParam announceParamCfg;
    ret = g_announceInstance->GetDefaultAnnounceParamConfig(announceParamCfg);
    memcpy(announceConfig, &announceParamCfg, sizeof(SleAnnounceParam));
    SLE_ASSERT_AND_RETURN_RET(ret == SLE_NO_ERROR, ret);
    return SLE_NO_ERROR;
}


SleErrorCode RegisterAnnounceCallbacks(SleAnnounceCallbacks *announceCallback)
{
    SLE_LOGI("RegisterAnnounceCallbacks In");
    SLE_ASSERT_AND_RETURN_RET_LOG(announceCallback, SLE_ERR_INVALID_PARAM, "announce callback error!");
    SleErrorCode ret = CheckAnnounceOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    std::lock_guard<std::mutex> lock(g_announceCbLock);
    for(int i = 0; i < SLE_MAX_ANNOUNCE_CALLBACK_NUM; i++) {
        SLE_ASSERT_AND_RETURN_RET_LOG(
            g_announceCallbacks[i] != announceCallback, SLE_ERR_INVALID_PARAM, "announce callback has registered!");
    }
    ret = SLE_ERR_FAILED;
    for(int i = 0; i < SLE_MAX_ANNOUNCE_CALLBACK_NUM; i++) {
        if (g_announceCallbacks[i] == nullptr) {
            g_announceCallbacks[i] = announceCallback;
            ret = SLE_NO_ERROR;
            break;
        }
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "register announce callback failed!");
    return SLE_NO_ERROR;
}

SleErrorCode UnregisterAnnounceCallbacks(SleAnnounceCallbacks *announceCallback)
{
    SLE_LOGI("UnregisterAnnounceCallbacks In");
    SLE_ASSERT_AND_RETURN_RET_LOG(announceCallback, SLE_ERR_INVALID_PARAM, "announce callback error!");
    SleErrorCode ret = CheckAnnounceOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    bool registered = false;
    std::lock_guard<std::mutex> lock(g_announceCbLock);
    for(int i = 0; i < SLE_MAX_ANNOUNCE_CALLBACK_NUM; i++) {
        if (g_announceCallbacks[i] == announceCallback) {
            g_announceCallbacks[i] = nullptr;
            registered = true;
            break;
        }
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(registered == true, SLE_ERR_INVALID_PARAM, "announce callback has not registered!");
    return SLE_NO_ERROR;
}
} // SleStandard
} // NearLink
} // OHOS

#ifdef __cplusplus
}
#endif